using System;
using System.Runtime.InteropServices;

namespace Objectware.Silverlight.Testing.Runner.Utilities
{
	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class CrossAppDomainSingleton<T> : MarshalByRefObject where T : new()
	{
		private static readonly string AppDomainName = "Singleton AppDomain";
		private static T _instance = default(T);

		//TODO: Use mutex to get cross-appdomain lock?
		private static readonly object SyncLock = new object();

		/// <summary>
		/// Initializes a new instance of the <see cref="CrossAppDomainSingleton&lt;T&gt;"/> class.
		/// </summary>
		public CrossAppDomainSingleton()
			: base()
		{
		}

		/// <summary>
		/// Releases unmanaged resources and performs other cleanup operations before the
		/// <see cref="CrossAppDomainSingleton&lt;T&gt;"/> is reclaimed by garbage collection.
		/// </summary>
		~CrossAppDomainSingleton()
		{
		}

		/// <summary>
		/// Obtains a lifetime service object to control the lifetime policy for this instance. This should return null for singleton classes.
		/// </summary>
		/// <returns>
		/// An object of type <see cref="T:System.Runtime.Remoting.Lifetime.ILease"></see> used to control the lifetime policy for this instance. This is the current lifetime service object for this instance if one exists; otherwise, a new lifetime service object initialized to the value of the <see cref="P:System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseManagerPollTime"></see> property.
		/// </returns>
		/// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
		/// <PermissionSet><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="RemotingConfiguration, Infrastructure"/></PermissionSet>
		public override object InitializeLifetimeService()
		{
			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="friendlyName"></param>
		/// <returns></returns>
		private static AppDomain GetAppDomain(string friendlyName)
		{
			IntPtr enumHandle = IntPtr.Zero;
			mscoree.CorRuntimeHostClass host = new mscoree.CorRuntimeHostClass();
			try
			{
				host.EnumDomains(out enumHandle);

				object domain = null;
				while (true)
				{
					host.NextDomain(enumHandle, out domain);
					if (domain == null)
					{
						break;
					}
					AppDomain appDomain = (AppDomain)domain;
					if (appDomain.FriendlyName.Equals(friendlyName))
					{
						return appDomain;
					}
				}
			}
			finally
			{
				host.CloseEnum(enumHandle);
				Marshal.ReleaseComObject(host);
				host = null;
			}
			return null;
		}

		/// <summary>
		/// Gets or sets the instance.
		/// </summary>
		/// <value>The instance.</value>
		public static T Instance
		{
			get
			{
				if (null == _instance)
				{
					lock (SyncLock)
					{
						if (null == _instance)
						{
							AppDomain appDomain = GetAppDomain(AppDomainName);
							if (null == appDomain)
							{

								appDomain = AppDomain.CreateDomain(AppDomainName);
								appDomain.InitializeLifetimeService();
							}
							Type type = typeof(T);

							T instance = (T)appDomain.GetData(type.FullName);
							if (null == instance)
							{
								instance = (T)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
								appDomain.SetData(type.FullName, instance);
							}

							_instance = instance;
						}
					}
				}
				return _instance;
			}
		}
	}
}